#ifndef SCHEDULER_H_INCLUDED
#define SCHEDULER_H_INCLUDED
#include "graphbuilder.h"
#include "../Entities/classroom.h"
#include "colors.h"
#include <map>

namespace Scheduler{
    typedef std::vector<Classroom> ClassroomList;
    bool checkAdjacentVertices(Colors::TimeSlots color, GraphBuilder::SessionList sessions, int vertex, int *matrix[]){
        int size = sessions.size();
        for(int i = 0; i < size; i++){
            if(i!=vertex){
                if(matrix[vertex][i] == 1){
                    if(sessions[i].allotedTime == color)
                        return false;
                }
            }
        }

        return true;
    }
    GraphBuilder::SessionList schedule(GraphBuilder::StudentGroupList list, int numberOfClassrooms){
        GraphBuilder::SessionList sessions = GraphBuilder::createSessions(list);
        int size = sessions.size();

        // initialize matrix
        //create2DArray(&matrix, size, size);
        int **matrix = new int*[size];
        for(int i = 0; i < size; i++){
            matrix[i] = new int[size];
        }

        // create adjacency matrix
        GraphBuilder::toMatrix(sessions, matrix);

        // do some initial setup before main algorithm
        Colors::reset();
        Colors::colorUseCountLimit = numberOfClassrooms;


        // main algorithm
        for(int i = 0; i < size; i++){
            Session *ptr = &sessions[i];

            if(checkAdjacentVertices(Colors::MW1, sessions, i, matrix) && (Colors::colorUseCount[Colors::MW1] < numberOfClassrooms)){
                ptr->allotedTime = Colors::MW1;
                Colors::colorUseCount[Colors::MW1]++;
            } else if(checkAdjacentVertices(Colors::TTH1, sessions, i, matrix)&& (Colors::colorUseCount[Colors::TTH1] < numberOfClassrooms)){
                ptr->allotedTime = Colors::TTH1;
                Colors::colorUseCount[Colors::TTH1]++;
            } else if(checkAdjacentVertices(Colors::MW2, sessions, i, matrix)&& (Colors::colorUseCount[Colors::MW2] < numberOfClassrooms)){
                ptr->allotedTime = Colors::MW2;
                Colors::colorUseCount[Colors::MW2]++;
            } else if(checkAdjacentVertices(Colors::TTH2, sessions, i, matrix)&& (Colors::colorUseCount[Colors::TTH2] < numberOfClassrooms)){
                ptr->allotedTime = Colors::TTH2;
                Colors::colorUseCount[Colors::TTH2]++;
            } else if(checkAdjacentVertices(Colors::MW3, sessions, i, matrix)&& (Colors::colorUseCount[Colors::MW3] < numberOfClassrooms)){
                ptr->allotedTime = Colors::MW3;
                Colors::colorUseCount[Colors::MW3]++;
            } else if(checkAdjacentVertices(Colors::TTH3, sessions, i, matrix)&& (Colors::colorUseCount[Colors::TTH3] < numberOfClassrooms)){
                ptr->allotedTime = Colors::TTH3;
                Colors::colorUseCount[Colors::TTH3]++;
            } else if(checkAdjacentVertices(Colors::MW4, sessions, i, matrix)&& (Colors::colorUseCount[Colors::MW4] < numberOfClassrooms)){
                ptr->allotedTime = Colors::MW4;
                Colors::colorUseCount[Colors::MW4]++;
            } else if(checkAdjacentVertices(Colors::TTH4, sessions, i, matrix)&& (Colors::colorUseCount[Colors::TTH4] < numberOfClassrooms)){
                ptr->allotedTime = Colors::TTH4;
                Colors::colorUseCount[Colors::TTH4]++;
            } else if(checkAdjacentVertices(Colors::MW5, sessions, i, matrix)&& (Colors::colorUseCount[Colors::MW5] < numberOfClassrooms)){
                ptr->allotedTime = Colors::MW5;
                Colors::colorUseCount[Colors::MW5]++;
            } else if(checkAdjacentVertices(Colors::TTH5, sessions, i, matrix)&& (Colors::colorUseCount[Colors::TTH5] < numberOfClassrooms)){
                ptr->allotedTime = Colors::TTH5;
                Colors::colorUseCount[Colors::TTH5]++;
            } else if(checkAdjacentVertices(Colors::MW6, sessions, i, matrix)&& (Colors::colorUseCount[Colors::MW6] < numberOfClassrooms)){
                ptr->allotedTime = Colors::MW6;
                Colors::colorUseCount[Colors::MW6]++;
            } else if(checkAdjacentVertices(Colors::TTH6, sessions, i, matrix)&& (Colors::colorUseCount[Colors::TTH6] < numberOfClassrooms)){
                ptr->allotedTime = Colors::TTH6;
                Colors::colorUseCount[Colors::TTH6]++;
            } else if(checkAdjacentVertices(Colors::MW7, sessions, i, matrix)&& (Colors::colorUseCount[Colors::MW7] < numberOfClassrooms)){
                ptr->allotedTime = Colors::MW7;
                Colors::colorUseCount[Colors::MW7]++;
            } else if(checkAdjacentVertices(Colors::TTH7, sessions, i, matrix)&& (Colors::colorUseCount[Colors::TTH7] < numberOfClassrooms)){
                ptr->allotedTime = Colors::TTH7;
                Colors::colorUseCount[Colors::TTH7]++;
            } else if(checkAdjacentVertices(Colors::TTH8, sessions, i, matrix)&& (Colors::colorUseCount[Colors::TTH8] < numberOfClassrooms)){
                ptr->allotedTime = Colors::TTH8;
                Colors::colorUseCount[Colors::TTH8]++;
            }
        }


        // cleanup
        //delete2DArray(&matrix, size, size);
        for(int i = 0; i < size; i++)
            delete[] matrix[i];
        delete[] matrix;
        return sessions;
    }

    struct CompleteSession{
        inline CompleteSession(Session a, Classroom b)
            : session(a), classroom(b) {}
        Session session;
        Classroom classroom;
    };
    typedef std::vector<CompleteSession> CompleteSessionList;

    CompleteSessionList assignClassroom(const GraphBuilder::SessionList sessions, const ClassroomList list){
        CompleteSessionList ret;

        int cSize = list.size();

        // create a 2D array to keep track of classrooms already used
        int **flags = new int*[Colors::colorCount];
        for(int c = 0; c < Colors::colorCount; c++)
            flags[c] = new int[cSize];

        // initialize 2D array
        for(int k = 0; k < Colors::colorCount; k++)
            for(int l = 0; l < cSize; l++)
                flags[k][l] = 0;

        // assigning of classrooms
        int sSize = sessions.size();

        for(int i = 0; i < sSize; i++){
            for(int t = 0; t < cSize; t++){
                if(flags[sessions[i].allotedTime][t] == 0){
                    CompleteSession a(sessions[i], list[t]);
                    flags[sessions[i].allotedTime][t] = 1;
                    ret.push_back(a);
                    break;
                }
            }
        }

        // cleanup
        for(int j = 0; j < Colors::colorCount; j++)
            delete[] flags[j];
        delete[] flags;
        return ret;
    }
    typedef std::map<int, CompleteSessionList> ScheduleByGroupList;

    ScheduleByGroupList sortScheduleByGroup(const CompleteSessionList schedules){
        ScheduleByGroupList ret;
        int size = schedules.size();
        for(int i = 0; i < size; i++){
            CompleteSession cs = schedules[i];
            if(ret.find(cs.session.group.id()) == ret.end()){
                CompleteSessionList insert;
                insert.push_back(cs);
                ret.insert(std::pair<int, CompleteSessionList>(cs.session.group.id(), insert));
            }else{
                ScheduleByGroupList::iterator it = ret.find(cs.session.group.id());
                it->second.push_back(cs);
            }
        }
        return ret;
    }
}

#endif // SCHEDULER_H_INCLUDED
